#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <time.h>
#include "comdef.h"

//#define DEBUG 1
#define NUM_INPUTS (2)
#define NUM_EXAMPLES (1000)
#define ETA (0.01)
#define NUM_EPOCHS (5000)

void data_iter(
    uint64_t num_examples, 
    uint64_t batch_size, 
    uint64_t *batch_index)
{
    for(uint64_t i = 0;i<batch_size;i++)
    {
        batch_index[i] = rand() % num_examples;
    }

}

int linear(void)
{

    const double true_w[2] = {12, -3.4};
    const double true_b = 4.2;

    double actual_w[2] = {0, 0};
    double actual_b = 0;

    const uint64_t batch_size = 10;

    double features[NUM_EXAMPLES][NUM_INPUTS];
    srand((unsigned)time(0));
    for(uint64_t i=0;i<NUM_EXAMPLES;i++)
    {
        for(uint64_t j=0;j<NUM_INPUTS;j++)
        {
            features[i][j] = (double)rand() / RAND_MAX * 10;
        }
    }

    double labels[NUM_EXAMPLES];
    for(uint64_t i=0;i<NUM_EXAMPLES;i++)
    {
        labels[i] = true_w[0]*features[i][0] + true_w[1]*features[i][1] + true_b;
        labels[i] += (((double)rand()) / RAND_MAX * 1 - 0.5);
    }

#ifdef DEBUG
    HEAD_LINE("labels and features");
    for(uint64_t i=0;i<NUM_EXAMPLES;i++)
    {
        printf("labels[%ld]:%f, ", i, labels[i]);
        for(uint64_t j=0;j<NUM_INPUTS;j++)
        {
            printf("features[%ld][%ld]:%f", i, j, features[i][j]);
        }
        printf("\r\n");
    }
#endif

    uint64_t batch_index[batch_size];
    data_iter(NUM_EXAMPLES, batch_size, batch_index);

#ifdef DEBUG
    HEAD_LINE("batch_index");
    for(uint64_t i = 0;i<batch_size;i++)
    {
        printf("batch_index[%ld]:%ld\r\n", i, batch_index[i]);
    }
#endif

    HEAD_LINE("true_w and true_b");
    printf("true_w[0]:%f, true_w[1]:%f, true_b:%f\r\n", true_w[0], true_w[1], true_b);


    /* 计算actual_w变化值 */
    for(uint64_t epoch = 0;epoch < NUM_EPOCHS;epoch++)
    {
        data_iter(NUM_EXAMPLES, batch_size, batch_index);

        double delta_w[NUM_INPUTS] = {0.0, 0.0};
        double delta_b = 0.0;

        double gradient[NUM_INPUTS+1] = {0.0, 0.0, 0.0};
        for(uint64_t i=0;i<batch_size;i++)
        {
            gradient[0] += features[batch_index[i]][0] * (features[batch_index[i]][0] * actual_w[0] + features[batch_index[i]][1] * actual_w[1] + actual_b - labels[batch_index[i]]);
        }
        delta_w[0] = gradient[0] * ETA / batch_size;
        actual_w[0] -= delta_w[0];

        for(uint64_t i=0;i<batch_size;i++)
        {
            gradient[1] += features[batch_index[i]][1] * (features[batch_index[i]][0] * actual_w[0] + features[batch_index[i]][1] * actual_w[1] + actual_b - labels[batch_index[i]]);
        }
        delta_w[1] = gradient[1] * ETA / batch_size;
        actual_w[1] -= delta_w[1];

        for(uint64_t i=0;i<batch_size;i++)
        {
            gradient[2] += (features[batch_index[i]][0] * actual_w[0] + features[batch_index[i]][1] * actual_w[1] + actual_b - labels[batch_index[i]]);
        }
        delta_b = gradient[2] * ETA / batch_size;
        actual_b -= delta_b;
    }
    HEAD_LINE("actual_w and actual_b");
    printf("actual_w[0]:%f, actual_w[1]:%f, actual_b:%f\r\n", actual_w[0], actual_w[1], actual_b);
    return 0;
}


int main(int argc, char **argv)
{
    UNUSED(argc);

    printf("%s\r\n", argv[0]);

    linear();
    return 0;
}


